import { mapState } from 'vuex'
import { partServer } from 'anov-core'
const stickSize = 8
const styleMapping = {
  y: {
    t: 'top',
    m: 'marginTop',
    b: 'bottom'
  },
  x: {
    l: 'left',
    m: 'marginLeft',
    r: 'right'
  }
}
export default {
  created() {
    this.stickDrag = false
    this.bodyDrag = false
    this.stickAxis = null
    this.stickStartPos = { mouseX: 0, mouseY: 0, x: 0, y: 0, w: 0, h: 0 }
    this.limits = {
      minLeft: null,
      maxLeft: null,
      minRight: null,
      maxRight: null,
      minTop: null,
      maxTop: null,
      minBottom: null,
      maxBottom: null
    }
    this.currentStick = []
  },
  mounted() {
    this.$nextTick(() => {
      this.vdrInit()
    })
  },
  beforeDestroy() {
    document.documentElement.removeEventListener('mousemove', this.move)
    document.documentElement.removeEventListener('mouseup', this.up)
    document.documentElement.removeEventListener('mouseleave', this.up)
    document.documentElement.removeEventListener('mousedown', this.deselect)
    document.documentElement.removeEventListener('touchmove', this.move, true)
    document.documentElement.removeEventListener(
      'touchend touchcancel',
      this.up,
      true
    )
    document.documentElement.removeEventListener('touchstart', this.up, true)
  },
  methods: {
    vdrInit() {
      this.parentElement = this.$el.parentNode
      this.parentWidth = this.parentElement.clientWidth
      this.parentHeight = this.parentElement.clientHeight
      // 部件右部定位
      if (this.anovPart.style.right || this.anovPart.style.right == 0) {
        this.rawLeft =
          this.parentWidth - this.rawWidth - parseInt(this.anovPart.style.right)
        this.left = this.rawLeft
        this.anovPart.style.left = this.rawLeft + 'px'
      }
      // 部件底部定位
      if (this.anovPart.style.bottom || this.anovPart.style.bottom == 0) {
        this.rawTop =
          this.parentHeight -
          this.rawHeight -
          parseInt(this.anovPart.style.bottom)
        this.top = this.rawTop
        this.anovPart.style.top = this.rawTop + 'px'
      }
      this.rawRight = this.parentWidth - this.rawWidth - this.rawLeft
      this.rawBottom = this.parentHeight - this.rawHeight - this.rawTop
      //  事件绑定
      document.documentElement.addEventListener('mousemove', this.move)
      document.documentElement.addEventListener('mouseup', this.up)
      document.documentElement.addEventListener('mouseleave', this.up)
      document.documentElement.addEventListener('mousedown', this.deselect)
      document.documentElement.addEventListener('touchmove', this.move, true)
      document.documentElement.addEventListener(
        'touchend touchcancel',
        this.up,
        true
      )
      document.documentElement.addEventListener('touchstart', this.up, true)
      if (this.dragHandle) {
        let dragHandles = Array.prototype.slice.call(
          this.$el.querySelectorAll(this.dragHandle)
        )
        for (let i in dragHandles) {
          dragHandles[i].setAttribute('data-drag-handle', this._uid)
        }
      }
      if (this.dragCancel) {
        let cancelHandles = Array.prototype.slice.call(
          this.$el.querySelectorAll(this.dragCancel)
        )
        for (let i in cancelHandles) {
          cancelHandles[i].setAttribute('data-drag-cancel', this._uid)
        }
      }
    },
    deselect() {
      if (this.preventActiveBehavior) {
        return
      }
      this.active = false
    },

    move(ev) {
      if (!this.stickDrag && !this.bodyDrag) {
        return
      }
      ev.stopPropagation()
      if (this.stickDrag) {
        this.stickMove(ev)
        let hScale =
          parseInt(this.rect.height) / parseInt(this.anovPart.style.height)
        let wScale =
          parseInt(this.rect.width) / parseInt(this.anovPart.style.width)
        let scale = hScale >= wScale ? wScale : hScale
        this.childrenScale = scale * this.anovPart.scale
        this.$children[0].$el.style.width =
          this.$children[0].$el.offsetWidth + 'px'
        this.$children[0].$el.style.height =
          this.$children[0].$el.offsetHeight + 'px'
        this.$children[0].$el.style.transform = `scale(${this.childrenScale})`
        this.$children[0].$el.style.transformOrigin = `0 0`
      }
      if (this.bodyDrag) {
        this.bodyMove(ev)
      }
      // 修改vuex值  及时存储 回显数值需要
      if (!this.timer) {
        this.timer = setTimeout(() => {
          this.changePart({
            name: this.$route.name,
            id: this.anovPart.id,
            part: {
              style: this.rect,
              scale: this.childrenScale
            }
          })
          this.timer = null
        }, 1500)
      }
    },
    up(ev) {
      if (this.stickDrag) {
        this.stickUp(ev)
      }
      if (this.bodyDrag) {
        this.bodyUp(ev)
      }
    },

    bodyDown(ev) {
      // 是否开启拖拽
      if (!this.isDragOpen) {
        this.active = false
        return
      }
      let parts = partServer.getCompByTagName('anovPart')
      parts.forEach(part => {
        part.active = false
      })
      this.active = true
      let target = ev.target || ev.srcElement
      if (!this.preventActiveBehavior) {
        this.active = true
      }
      if (ev.button && ev.button !== 0) {
        return
      }
      if (!this.isDraggable || !this.active) {
        return
      }

      if (
        this.dragHandle &&
        target.getAttribute('data-drag-handle') !== this._uid.toString()
      ) {
        return
      }

      if (
        this.dragCancel &&
        target.getAttribute('data-drag-cancel') === this._uid.toString()
      ) {
        return
      }
      ev.stopPropagation()
      ev.preventDefault()
      this.bodyDrag = true
      this.stickStartPos.mouseX =
        typeof ev.pageX !== 'undefined' ? ev.pageX : ev.touches[0].pageX
      this.stickStartPos.mouseY =
        typeof ev.pageY !== 'undefined' ? ev.pageY : ev.touches[0].pageY

      this.stickStartPos.left = this.left
      this.stickStartPos.right = this.right
      this.stickStartPos.top = this.top
      this.stickStartPos.bottom = this.bottom

      if (this.parentLimitation) {
        this.limits = this.calcDragLimitation()
      }
    },

    calcDragLimitation() {
      const parentWidth = this.parentWidth
      const parentHeight = this.parentHeight
      return {
        minLeft: 0,
        maxLeft: parentWidth - this.width,
        minRight: 0,
        maxRight: parentWidth - this.width,
        minTop: 0,
        maxTop: parentHeight - this.height,
        minBottom: 0,
        maxBottom: parentHeight - this.height
      }
    },

    bodyMove(ev) {
      const stickStartPos = this.stickStartPos
      const parentWidth = this.parentWidth
      const parentHeight = this.parentHeight
      const gridX = this.gridX
      const gridY = this.gridY
      const width = this.width
      const height = this.height
      const pageX =
        typeof ev.pageX !== 'undefined' ? ev.pageX : ev.touches[0].pageX
      const pageY =
        typeof ev.pageY !== 'undefined' ? ev.pageY : ev.touches[0].pageY

      let delta = {
        x:
          (this.axis !== 'y' && this.axis !== 'none'
            ? stickStartPos.mouseX - pageX
            : 0) / this.parentScaleX,
        y:
          (this.axis !== 'x' && this.axis !== 'none'
            ? stickStartPos.mouseY - pageY
            : 0) / this.parentScaleY
      }
      let newTop = stickStartPos.top - delta.y
      let newBottom = stickStartPos.bottom + delta.y
      let newLeft = stickStartPos.left - delta.x
      let newRight = stickStartPos.right + delta.x

      if (this.snapToGrid) {
        let alignTop = true
        let alignLeft = true

        let diffT = newTop - Math.floor(newTop / gridY) * gridY
        let diffB =
          parentHeight -
          newBottom -
          Math.floor((parentHeight - newBottom) / gridY) * gridY
        let diffL = newLeft - Math.floor(newLeft / gridX) * gridX
        let diffR =
          parentWidth -
          newRight -
          Math.floor((parentWidth - newRight) / gridX) * gridX

        if (diffT > gridY / 2) {
          diffT = diffT - gridY
        }
        if (diffB > gridY / 2) {
          diffB = diffB - gridY
        }
        if (diffL > gridX / 2) {
          diffL = diffL - gridX
        }
        if (diffR > gridX / 2) {
          diffR = diffR - gridX
        }

        if (Math.abs(diffB) < Math.abs(diffT)) {
          alignTop = false
        }
        if (Math.abs(diffR) < Math.abs(diffL)) {
          alignLeft = false
        }

        newTop = newTop - (alignTop ? diffT : diffB)
        newBottom = parentHeight - height - newTop
        newLeft = newLeft - (alignLeft ? diffL : diffR)
        newRight = parentWidth - width - newLeft
      }

      this.rawTop = newTop
      this.rawBottom = newBottom
      this.rawLeft = newLeft
      this.rawRight = newRight
    },

    bodyUp() {
      this.bodyDrag = false
      this.stickStartPos = { mouseX: 0, mouseY: 0, x: 0, y: 0, w: 0, h: 0 }
      this.limits = {
        minLeft: null,
        maxLeft: null,
        minRight: null,
        maxRight: null,
        minTop: null,
        maxTop: null,
        minBottom: null,
        maxBottom: null
      }
    },

    stickDown: function(stick, ev) {
      if (!this.isResizable || !this.active) {
        return
      }
      this.stickDrag = true
      this.stickStartPos.mouseX =
        typeof ev.pageX !== 'undefined' ? ev.pageX : ev.touches[0].pageX
      this.stickStartPos.mouseY =
        typeof ev.pageY !== 'undefined' ? ev.pageY : ev.touches[0].pageY
      this.stickStartPos.left = this.left
      this.stickStartPos.right = this.right
      this.stickStartPos.top = this.top
      this.stickStartPos.bottom = this.bottom
      this.currentStick = stick.split('')
      this.stickAxis = null
      switch (this.currentStick[0]) {
        case 'b':
          this.stickAxis = 'y'
          break
        case 't':
          this.stickAxis = 'y'
          break
      }
      switch (this.currentStick[1]) {
        case 'r':
          this.stickAxis = this.stickAxis === 'y' ? 'xy' : 'x'
          break
        case 'l':
          this.stickAxis = this.stickAxis === 'y' ? 'xy' : 'x'
          break
      }

      this.limits = this.calcResizeLimitation()
    },

    calcResizeLimitation() {
      let minw = this.minWidth
      let minh = this.minHeight
      const aspectFactor = this.aspectFactor
      const width = this.width
      const height = this.height
      const bottom = this.bottom
      const top = this.top
      const left = this.left
      const right = this.right
      const stickAxis = this.stickAxis
      const parentLim = this.parentLimitation ? 0 : null
      if (this.aspectRatio) {
        if (minw / minh > aspectFactor) {
          minh = minw / aspectFactor
        } else {
          minw = aspectFactor * minh
        }
      }

      let limits = {
        minLeft: parentLim,
        maxLeft: left + (width - minw),
        minRight: parentLim,
        maxRight: right + (width - minw),
        minTop: parentLim,
        maxTop: top + (height - minh),
        minBottom: parentLim,
        maxBottom: bottom + (height - minh)
      }

      if (this.aspectRatio) {
        const aspectLimits = {
          minLeft: left - Math.min(top, bottom) * aspectFactor * 2,
          maxLeft: left + ((height - minh) / 2) * aspectFactor * 2,

          minRight: right - Math.min(top, bottom) * aspectFactor * 2,
          maxRight: right + ((height - minh) / 2) * aspectFactor * 2,

          minTop: top - (Math.min(left, right) / aspectFactor) * 2,
          maxTop: top + ((width - minw) / 2 / aspectFactor) * 2,

          minBottom: bottom - (Math.min(left, right) / aspectFactor) * 2,
          maxBottom: bottom + ((width - minw) / 2 / aspectFactor) * 2
        }

        if (stickAxis === 'x') {
          limits = {
            minLeft: Math.max(limits.minLeft, aspectLimits.minLeft),
            maxLeft: Math.min(limits.maxLeft, aspectLimits.maxLeft),
            minRight: Math.max(limits.minRight, aspectLimits.minRight),
            maxRight: Math.min(limits.maxRight, aspectLimits.maxRight)
          }
        } else if (stickAxis === 'y') {
          limits = {
            minTop: Math.max(limits.minTop, aspectLimits.minTop),
            maxTop: Math.min(limits.maxTop, aspectLimits.maxTop),
            minBottom: Math.max(limits.minBottom, aspectLimits.minBottom),
            maxBottom: Math.min(limits.maxBottom, aspectLimits.maxBottom)
          }
        }
      }

      return limits
    },

    stickMove(ev) {
      const stickStartPos = this.stickStartPos
      const pageX =
        typeof ev.pageX !== 'undefined' ? ev.pageX : ev.touches[0].pageX
      const pageY =
        typeof ev.pageY !== 'undefined' ? ev.pageY : ev.touches[0].pageY

      const delta = {
        x: (stickStartPos.mouseX - pageX) / this.parentScaleX,
        y: (stickStartPos.mouseY - pageY) / this.parentScaleY
      }

      let newTop = stickStartPos.top - delta.y
      let newBottom = stickStartPos.bottom + delta.y
      let newLeft = stickStartPos.left - delta.x
      let newRight = stickStartPos.right + delta.x

      switch (this.currentStick[0]) {
        case 'b':
          if (this.snapToGrid) {
            newBottom =
              this.parentHeight -
              Math.round((this.parentHeight - newBottom) / this.gridY) *
                this.gridY
          }

          this.rawBottom = newBottom
          break

        case 't':
          if (this.snapToGrid) {
            newTop = Math.round(newTop / this.gridY) * this.gridY
          }

          this.rawTop = newTop
          break
      }

      switch (this.currentStick[1]) {
        case 'r':
          if (this.snapToGrid) {
            newRight =
              this.parentWidth -
              Math.round((this.parentWidth - newRight) / this.gridX) *
                this.gridX
          }

          this.rawRight = newRight
          break

        case 'l':
          if (this.snapToGrid) {
            newLeft = Math.round(newLeft / this.gridX) * this.gridX
          }

          this.rawLeft = newLeft
          break
      }
    },

    stickUp() {
      this.stickDrag = false
      this.stickStartPos = {
        mouseX: 0,
        mouseY: 0,
        x: 0,
        y: 0,
        w: 0,
        h: 0
      }
      this.limits = {
        minLeft: null,
        maxLeft: null,
        minRight: null,
        maxRight: null,
        minTop: null,
        maxTop: null,
        minBottom: null,
        maxBottom: null
      }
      this.rawTop = this.top
      this.rawBottom = this.bottom
      this.rawLeft = this.left
      this.rawRight = this.right

      this.stickAxis = null

      // this.$emit('resizing', this.rect)
      // this.$emit('resizestop', this.rect)
    },

    aspectRatioCorrection() {
      if (!this.aspectRatio) {
        return
      }

      const bottom = this.bottom
      const top = this.top
      const left = this.left
      const right = this.right
      const width = this.width
      const height = this.height
      const aspectFactor = this.aspectFactor
      const currentStick = this.currentStick

      if (width / height > aspectFactor) {
        let newWidth = aspectFactor * height

        if (currentStick[1] === 'l') {
          this.left = left + width - newWidth
        } else {
          this.right = right + width - newWidth
        }
      } else {
        let newHeight = width / aspectFactor

        if (currentStick[0] === 't') {
          this.top = top + height - newHeight
        } else {
          this.bottom = bottom + height - newHeight
        }
      }
    }
  },
  computed: {
    ...mapState('partServer', ['isDragOpen']),
    style() {
      return {
        top: this.top + 'px',
        left: this.left + 'px',
        width: this.width + 'px',
        height: this.height + 'px',
        zIndex: this.zIndex
      }
    },
    vdrStick() {
      return stick => {
        const stickStyle = {
          width: `${stickSize / this.parentScaleX}px`,
          height: `${stickSize / this.parentScaleY}px`
        }
        stickStyle[styleMapping.y[stick[0]]] = `${stickSize /
          this.parentScaleX /
          -2}px`
        stickStyle[styleMapping.x[stick[1]]] = `${stickSize /
          this.parentScaleX /
          -2}px`
        return stickStyle
      }
    },

    width() {
      return this.parentWidth - this.left - this.right
    },

    height() {
      return this.parentHeight - this.top - this.bottom
    },

    rect() {
      return {
        left: Math.round(this.left) + 'px',
        top: Math.round(this.top) + 'px',
        width: Math.round(this.width) + 'px',
        height: Math.round(this.height) + 'px'
      }
    }
  },
  watch: {
    isDragOpen: {
      handler: function(v) {
        if (!v) this.active = false
      },
      immediate: false
    },
    rawLeft(newLeft) {
      const limits = this.limits
      const stickAxis = this.stickAxis
      const aspectFactor = this.aspectFactor
      const aspectRatio = this.aspectRatio
      const left = this.left
      const bottom = this.bottom
      const top = this.top

      if (limits.minLeft !== null && newLeft < limits.minLeft) {
        newLeft = limits.minLeft
      } else if (limits.maxLeft !== null && limits.maxLeft < newLeft) {
        newLeft = limits.maxLeft
      }

      if (aspectRatio && stickAxis === 'x') {
        const delta = left - newLeft
        this.rawTop = top - delta / aspectFactor / 2
        this.rawBottom = bottom - delta / aspectFactor / 2
      }

      this.left = newLeft
    },

    rawRight(newRight) {
      const limits = this.limits
      const stickAxis = this.stickAxis
      const aspectFactor = this.aspectFactor
      const aspectRatio = this.aspectRatio
      const right = this.right
      const bottom = this.bottom
      const top = this.top

      if (limits.minRight !== null && newRight < limits.minRight) {
        newRight = limits.minRight
      } else if (limits.maxRight !== null && limits.maxRight < newRight) {
        newRight = limits.maxRight
      }

      if (aspectRatio && stickAxis === 'x') {
        const delta = right - newRight
        this.rawTop = top - delta / aspectFactor / 2
        this.rawBottom = bottom - delta / aspectFactor / 2
      }

      this.right = newRight
    },

    rawTop(newTop) {
      const limits = this.limits
      const stickAxis = this.stickAxis
      const aspectFactor = this.aspectFactor
      const aspectRatio = this.aspectRatio
      const right = this.right
      const left = this.left
      const top = this.top

      if (limits.minTop !== null && newTop < limits.minTop) {
        newTop = limits.minTop
      } else if (limits.maxTop !== null && limits.maxTop < newTop) {
        newTop = limits.maxTop
      }

      if (aspectRatio && stickAxis === 'y') {
        const delta = top - newTop
        this.rawLeft = left - (delta * aspectFactor) / 2
        this.rawRight = right - (delta * aspectFactor) / 2
      }

      this.top = newTop
    },

    rawBottom(newBottom) {
      const limits = this.limits
      const stickAxis = this.stickAxis
      const aspectFactor = this.aspectFactor
      const aspectRatio = this.aspectRatio
      const right = this.right
      const left = this.left
      const bottom = this.bottom

      if (limits.minBottom !== null && newBottom < limits.minBottom) {
        newBottom = limits.minBottom
      } else if (limits.maxBottom !== null && limits.maxBottom < newBottom) {
        newBottom = limits.maxBottom
      }

      if (aspectRatio && stickAxis === 'y') {
        const delta = bottom - newBottom
        this.rawLeft = left - (delta * aspectFactor) / 2
        this.rawRight = right - (delta * aspectFactor) / 2
      }

      this.bottom = newBottom
    },

    width() {
      this.aspectRatioCorrection()
    },

    height() {
      this.aspectRatioCorrection()
    },

    z(val) {
      if (val >= 0 || val === 'auto') {
        this.zIndex = val
      }
    },
    aspectRatio(val) {
      if (val) {
        this.aspectFactor = this.width / this.height
      }
    },

    minw(val) {
      if (val > 0 && val <= this.width) {
        this.minWidth = val
      }
    },

    minh(val) {
      if (val > 0 && val <= this.height) {
        this.minHeight = val
      }
    }
  }
}
